﻿//------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Description: Validation Rule to validate that the specified tag having 
//                specified inner text exists on the page 
//
//------------------------------------------------------------------------------
using System;
using System.ComponentModel;
using Microsoft.VisualStudio.TestTools.WebTesting;
using System.Xml;
using HtmlAgilityPack;

namespace WMIS.Test.Common.VSTS.WebTest
{
    [DisplayName("Tag and innerText")]
    [Description("Validates that the specified tag having specified inner text exists on the page.")]
    public class ValidateTagValue : ValidationRule
    {
        // The name of the required tag
        private string tagName;

        [DisplayName("Required Tag Name")]
        [Description("The name of the required tag")]
        public string RequiredtagName
        {
            get { return tagName; }
            set { tagName = value; }
        }

        // The inner text of the required tag
        private string innerText;

        [DisplayName("Inner Text")]
        [Description("The expected Inner Text of the required tag. The found Inner Text is trimmed before comparison.")]
        public string SpecifiedinnerText
        {
            get { return innerText; }
            set { innerText = value; }
        }

        // The number of children that the tag has
        private int numberOfChildrenPresent = -1;

        [DisplayName("Number of Children")]
        [Description("The tag's expected number of children.")]
        [DefaultValue(-1)]
        public int NumberOfChildren
        {
            get { return numberOfChildrenPresent; }
            set { numberOfChildrenPresent = value; }
        }

        //  Should the Case be ignored while comparing the innerText
        private bool ignoreCaseValue = false;

        [DisplayName("Ignore Case")]
        [Description("Whether case should be ignored or not while comparing the inner text.")]
        [DefaultValue(false)]
        public bool IgnoreCase
        {
            get { return ignoreCaseValue; }
            set { ignoreCaseValue = value; }
        }

        // Validate is called with the test case Context and the request context.
        // These allow the rule to examine both the request and the response.
        //---------------------------------------------------------------------
        public override void Validate(object sender, ValidationEventArgs e)
        {
            bool validated = false;
            bool nodeFound = false;
            int childCount = 0;

            try
            {
                if (e.Response.IsXml.Equals(true) && tagName.Length != 0)
                {
                    //  It's an XML response
                    XmlNodeList xmlNodeList = e.Response.XmlDocument.GetElementsByTagName(tagName);

                    if (xmlNodeList.Count > 0)
                    {
                        nodeFound = true;
                    }

                    foreach (XmlNode xmlNode in xmlNodeList)
                    {
                        childCount = xmlNode.ChildNodes.Count;

                        e.Message = String.Format("innerText = '{0}', xmlNode.innerText = '{1}'", innerText, xmlNode.InnerText);

                        // Compare inner text only if the user has specified an innerText
                        if (innerText.Length == 0 || String.Equals(innerText, xmlNode.InnerText.Trim(), (ignoreCaseValue == true) ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture))
                        {
                            // Compare number of children if the user has specified a count
                            if (numberOfChildrenPresent == -1 || numberOfChildrenPresent == xmlNode.ChildNodes.Count)
                            {
                                //e.Message = String.Format("validated is true");
                                validated = true;
                                break;
                            }
                        }
                    }

                }
                else if (e.Response.IsHtml.Equals(true) && tagName.Length != 0)
                {
                    //  It's an HTML response
                    HtmlAgilityPack.HtmlDocument mainDocument = new HtmlAgilityPack.HtmlDocument();
                    mainDocument.LoadHtml(e.Response.BodyString);

                    HtmlNode mainNode = mainDocument.DocumentNode;

                    if (mainNode.SelectNodes(String.Format("//{0}", tagName)) != null)
                    {
                        foreach (HtmlNode htmlNode in mainNode.SelectNodes(String.Format("//{0}", tagName)))
                        {
                            nodeFound = true;
                            childCount = htmlNode.ChildNodes.Count;

                            e.Message = String.Format("innerText = '{0}', htmlNode.innerText = '{1}'", innerText, htmlNode.InnerText);

                            if (innerText.Length == 0 || String.Equals(innerText, htmlNode.InnerText.Trim(), (IgnoreCase == true) ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture))
                            {
                                // Compare number of children if the user has specified a count
                                if (numberOfChildrenPresent == -1 || numberOfChildrenPresent == htmlNode.ChildNodes.Count)
                                {
                                    //e.Message = String.Format("validated is true");
                                    validated = true;
                                    break;
                                }
                            }
                        }
                    }

                }

                e.IsValid = validated;

                // If the validation fails, set the error text that the user sees
                if (!validated)
                {
                    if (nodeFound == false)
                    {
                        e.Message = String.Format("The specified tag '{0}' was not found", tagName.ToString());
                    }
                    else if (numberOfChildrenPresent != -1 && numberOfChildrenPresent != childCount)
                    {
                        e.Message = String.Format("Expected '{0}' children, found '{1}' children for the specified tag", numberOfChildrenPresent, childCount);
                    }
                    else
                    {
                        //  Above, we're showing the user what was expected and what was found
                    }
                }
            }
            catch (Exception ex)
            {
                e.IsValid = validated;
                e.Message = String.Format("An exception occurred. Stack Dump: {0}", ex);
            }
            
            
        }
        
    }
}